package tianrun.ziguan.api.calculate.config.service.common.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import tianrun.ziguan.api.calculate.config.constant.StringPools;
import tianrun.ziguan.api.calculate.config.dao.common.CommonMapper;
import tianrun.ziguan.api.calculate.config.dto.request.common.CopyBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.DeleteBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.InfoBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.add.edit.AddOrEditBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.add.edit.AddOrEditBodyBatch;
import tianrun.ziguan.api.calculate.config.dto.request.common.add.edit.SaveBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.list.FilterField;
import tianrun.ziguan.api.calculate.config.dto.request.common.list.ListBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.list.PageBody;
import tianrun.ziguan.api.calculate.config.entity.ge.TGeTableConfig;
import tianrun.ziguan.api.calculate.config.service.common.CommonService;
import tianrun.ziguan.api.calculate.config.service.common.edit.DealService;
import tianrun.ziguan.api.calculate.config.service.ge.TGeTableConfigService;
import tianrun.ziguan.api.calculate.config.utils.CollectionUtil;
import tianrun.ziguan.api.calculate.config.utils.MapUtil;
import tianrun.ziguan.api.calculate.config.utils.StringUtil;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class CommonServiceImpl implements CommonService {

    private final TGeTableConfigService tableConfigService;
    private final CommonMapper commonMapper;
    private final ApplicationContext applicationContext;
    private final Map<String, DealService> map;

    public CommonServiceImpl(TGeTableConfigService tableConfigService, CommonMapper commonMapper, ApplicationContext applicationContext, List<DealService> dealServices) {
        this.tableConfigService = tableConfigService;
        this.commonMapper = commonMapper;
        this.applicationContext = applicationContext;
        this.map = dealServices.stream().collect(Collectors.toMap(DealService::tableName, Function.identity()));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Object addOrEdit(AddOrEditBody param) {
        return addOrEditNoTransactional(param);
    }

    public Object addOrEditNoTransactional(AddOrEditBody param) {
        DealService dealService = map.getOrDefault(param.getTableName(), map.get(StringPools.DEAL));
        SaveBody body = dealService.handler(param);
        // 必填校验
        dealService.before(body);
        if (body.isAdd()) {
            commonMapper.add(body);
        } else {
            commonMapper.edit(body);
        }
        dealService.after(body);
        return body.getId();
    }

    @Transactional
    @Override
    public Object addOrEditBatch(AddOrEditBodyBatch body) {
        List<AddOrEditBody> addOrEditBodies = body.toList();
        ArrayList<Object> objects = Lists.newArrayList();
        StringBuilder sb = new StringBuilder();
        addOrEditBodies.forEach(addOrEditBody -> {
            try {
                objects.add(addOrEditNoTransactional(addOrEditBody));
            } catch (IllegalArgumentException e) {
                sb.append(e.getMessage()).append(StringPool.NEWLINE);
            }
        });
        String message = sb.toString();
        org.apache.shiro.util.Assert.isTrue(message.isEmpty(), message.trim());
        return objects;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(DeleteBody body) {
        if (CollectionUtil.isEmpty(body.getIds())) return;
        DealService dealService = map.getOrDefault(body.getTableName(), map.get(StringPools.DEAL));
        // 必填校验
        dealService.delBefore(body);
        String primaryKey = tableConfigService.primaryKey(body.getTableName());
        Assert.hasText(primaryKey, body.getTableName() + "无相关配置！");
        commonMapper.delete(body.getTableName(), primaryKey, body.getIds());
        dealService.delAfter(body);
        applicationContext.publishEvent(body);
    }

    @Override
    public List<Map<String, Object>> list(ListBody body) {
        return list(body, true);
    }

    @Override
    public List<Map<String, Object>> list(ListBody body, Boolean after) {
        // todo 从表字段联表查询
        Map<String, TGeTableConfig.Type> configMap = tableConfigService.typeConfigs(body.getTableName());
        Assert.notEmpty(configMap, body.getTableName() + "无相关配置！");
        List<Map<String, Object>> list = commonMapper.list(body.to(configMap.keySet()));
        List<Map<String, Object>> result = list.stream().map(map -> this.typeHandler(map, configMap)).collect(Collectors.toList());
        DealService dealService = map.getOrDefault(body.getTableName(), map.get(StringPools.DEAL));
        if (after) {
            dealService.listAfter(result);
        }
        return result;
    }

    @Override
    public List<Map<String, Object>> listByIds(String tableName, Set<Long> ids, boolean isAll) {
        String primaryKey = tableConfigService.primaryKey(tableName);
        Assert.hasText(primaryKey, tableName + "无相关配置！");
        return this.listByIds(primaryKey, tableName, ids, isAll);
    }

    @Override
    public List<Map<String, Object>> listByIds(String primaryKey, String tableName, Set<Long> ids, boolean isAll) {
        ListBody listBody = new ListBody();
        listBody.setTableName(tableName);
        ArrayList<FilterField> filterFields = Lists.newArrayList(FilterField.in(primaryKey, ids));
        if (!isAll) {
            filterFields.add(FilterField.equal(StringPools.VALID, 1));
        }
        listBody.setFilterFields(filterFields);
        return this.list(listBody);
    }

    @Override
    public IPage<Map<String, Object>> page(PageBody body) {
        // todo 从表字段联表查询
        Map<String, TGeTableConfig.Type> configMap = tableConfigService.typeConfigs(body.getTableName());
        List<String> columns = Lists.newArrayList(configMap.keySet());
        Assert.notEmpty(columns, body.getTableName() + "无相关配置！");
        IPage<Map<String, Object>> page = commonMapper.page(new Page<>(body.getCurrentPage(), body.getSize()), body.to(configMap.keySet()));
        page.getRecords().forEach(map -> this.typeHandler(map, configMap));
        return page;
    }

    @Override
    public Map<String, Object> info(InfoBody body) {
        // todo 从表字段联表查询
        List<TGeTableConfig> configs = tableConfigService.listByTableName(body.getTableName());
        Optional<String> optional = configs.stream().filter(TGeTableConfig::getPrimaryKey).map(TGeTableConfig::getColumnName).findFirst();
        String key = optional.orElse(null);
        Assert.hasText(key, body.getTableName() + "无相关配置！");
        Map<String, Object> map1 = commonMapper.select(body.getTableName(), key, body.getDataId());
        Map<String, TGeTableConfig.Type> configMap = CollectionUtil.toMap(configs, TGeTableConfig::getColumnName, TGeTableConfig::getType);
        Map<String, Object> bean = typeHandler(map1, configMap);

        DealService dealService = map.getOrDefault(body.getTableName(), map.get(StringPools.DEAL));
        dealService.infoAfter(bean);
        return bean;
    }

    private Map<String, Object> typeHandler(Map<String, Object> map, Map<String, TGeTableConfig.Type> configMap) {
        configMap.forEach((column, value) -> {
            Object o = map.get(column);
            o = configMap.get(column).parseOut(o);
            map.put(column, o);
        });
        return map;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void copy(CopyBody body) {
        String tableName = body.getSource();
        Set<Long> ids = body.getIds();

        List<TGeTableConfig> configs = tableConfigService.listByTableName(tableName);
        Map<String, TGeTableConfig.Type> configMap = CollectionUtil.toMap(configs, TGeTableConfig::getColumnName, TGeTableConfig::getType);
        Optional<String> optional = configs.stream().filter(TGeTableConfig::getPrimaryKey).map(TGeTableConfig::getColumnName).findFirst();
        String primaryKey = optional.orElse(null);
        Assert.hasText(primaryKey, tableName + "无相关配置！");
        List<Map<String, Object>> list = this.listByIds(primaryKey, tableName, ids, false);

        String foreignKey = body.getForeignKey();
        Object parentId = body.getParentId();
        list.forEach(map -> {
            Long primary = MapUtil.getLong(map, primaryKey);
            Object remove = map.remove(primaryKey);
            map = map.entrySet().stream().collect(HashMap::new, (newMap, item) -> newMap.put(item.getKey(), configMap.getOrDefault(item.getKey(), TGeTableConfig.Type.STRING).parseIn(item.getValue())), HashMap::putAll);
            rename(map, body, remove);


            if (StringUtil.isNotNullAndBlank(foreignKey) && Objects.nonNull(parentId) && map.containsKey(foreignKey)) {
                map.put(foreignKey, parentId);
            }
            HashMap<String, Object> tempMap = Maps.newHashMap();
            for (Map.Entry<String, TGeTableConfig.Type> entry : configMap.entrySet()) {
                tempMap.put(entry.getKey(), map.get(entry.getKey()));
            }
            SaveBody saveBody = new SaveBody();
            saveBody.setData(tempMap);
            saveBody.setTableName(tableName);
            commonMapper.add(saveBody);
            CopyBody copyBody = JSONObject.parseObject(JSON.toJSONString(body), CopyBody.class);
            copyBody.setParentId(saveBody.getId());
            copyBody.setIds(Collections.singleton(primary));
            applicationContext.publishEvent(copyBody);
        });


    }

    private static void rename(Map<String, Object> map, CopyBody body, Object remove) {
        String rename = body.getRename();
        String tableName = body.getSource();
        if (StringUtil.isNullOrBlank(rename)) {
            return;
        }
        String name = MapUtil.getString(map, rename);
        String suffix = StringPool.UNDERSCORE + System.currentTimeMillis() + "_copy";
        if (StringUtil.isNullOrBlank(name)) {
            String prefix = tableName + StringPool.UNDERSCORE + remove;
            map.put(rename, prefix + suffix);
        } else {
            int i = name.indexOf(StringPool.UNDERSCORE);
            map.put(rename, (i != -1 ? name.substring(0, i) : name) + suffix);
        }
    }

}
